package bll

import (
	"context"
	"encoding/json"
	"github.com/golang-module/carbon/v2"
	log "simcard/collector/logger"
	"simcard/errors"
	"simcard/utils"

	"simcard/model"
	"simcard/model/entity"
	"simcard/model/mapping"
	"simcard/store"
	"simcard/store/postgres"
	"time"
)

type prepaid struct {
	iPrepaid      store.IPrepaid
	ISimCard      store.ISimCard
	ISimCardAgent store.ISimCardAgent
}

var Prepaid = &prepaid{
	iPrepaid:      postgres.Prepaid,
	ISimCard:      postgres.SimCard,
	ISimCardAgent: postgres.SimCardAgent,
}

func init() {
	Register(Prepaid)
}

func (a *prepaid) init() func() {
	return func() {}
}

func (a *prepaid) create(ctx context.Context, agentDetail *model.AgentDetail,
	respSimCard *entity.SimCard, in *model.PrepaidCreateRequest) error {

	c := buildPrepaid(in)
	respCardService, err := SimCard.GetSimCardServiceDetail(ctx, &model.GetSimCardServiceDetailRequest{EBikeId: in.EbikeId})
	if err != nil {
		return err
	}

	/*
	   如果是量讯,那么计算计费周期的规则是:
	   例如:
	     当前套餐有效期范围是:
	     计费起始日期:	2025年03月01日
	     计费结束日期:	2026年02月28日
	     在 2025年07月01日(只要是在 套餐有效期范围内 续费) 号续费之后的套餐有效期范围是:
	      计费起始日期:	2026年03月01日
	      计费结束日期:	2027年02月28日

	   将当前时间转化为东8区的时间，然后就行时间比较，不用管当前时间的时区
	*/

	var (
		agentType = model.AgentType(agentDetail.Code)
		next      = agentType.NextEffectiveRange(model.EffectiveRange{
			EffectiveDate: respCardService.StartDate,
			ExpiredDate:   respCardService.EndDate,
		})
	)

	if next.DaysToExpiry(agentType) < agentType.RemainDay() {
		c.Status = int(model.RepaidStatusRightNow)
	}

	c.EffectiveDate = next.EffectiveDate
	c.ExpiredDate = next.ExpiredDate
	c.Iccid = respSimCard.Iccid
	c.EffectiveDateUnix = carbon.ParseByFormat(next.EffectiveDate, "2006-01-02", carbon.PRC).Timestamp()
	c.ExpiredDateUnix = carbon.ParseByFormat(next.ExpiredDate, "2006-01-02", carbon.PRC).Timestamp()

	id, err := a.iPrepaid.Create(ctx, c)
	if err != nil {
		return err
	}

	if c.Status != int(model.RepaidStatusRightNow) {
		return nil
	}

	return SimCard.PreRecharge(ctx, &model.PreRechargeRequest{
		Mode: "async",
		Items: []*model.PreRechargeItem{
			{
				PrepaidId: utils.GetValuePointer(id),
				EbikeId:   utils.GetValuePointer(in.EbikeId),
			},
		},
	})
}

func (a *prepaid) Create(ctx context.Context, in *model.PrepaidCreateRequest) error {

	var (
		err          error
		respSimCards []*entity.SimCard
		respSimCard  *entity.SimCard
		agentDetail  = &model.AgentDetail{}
	)

	if _, respSimCards, err = a.ISimCard.List(ctx, &model.SimCardListRequest{
		Sn: utils.GetValuePointer(in.Sn),
	}, model.QueryOptions{OnlyList: true}); err != nil {
		return err
	} else {
		if len(respSimCards) < 0 {
			return errors.NotFound.Error()
		} else {
			respSimCard = respSimCards[0]

			_ = json.Unmarshal(respSimCard.AgentDetail, agentDetail)
		}
	}

	for range in.Years {

		if err = a.create(ctx, agentDetail, respSimCard, in); err != nil {
			return err
		}
	}

	return nil
}

func (a *prepaid) CheckForDelayRepaid(ctx context.Context, in *model.SimCardListRequest) error {

	var (
		size  = 200
		index = 1
	)

	for {
		var (
			err         error
			preRequests = &model.PreRechargeRequest{
				Mode: "async",
			}
		)
		respSimCards, err := SimCard.List(ctx, &model.SimCardListRequest{
			Index:    index,
			Size:     size,
			EbikeIds: in.EbikeIds,
			Expiring: utils.GetValuePointer(true),
		})
		if err != nil {
			return err
		}

		if len(respSimCards.List) <= 0 {
			break
		}

		var ebikeIds []int64
		for _, v := range respSimCards.List {
			ebikeIds = append(ebikeIds, v.EbikeId)
		}

		/* 检查预充值表是否有预充值，可能有多条预充值记录，有多少条就充值多少条 */
		_, respPrepaidList, err := a.iPrepaid.List(ctx, &model.PrepaidListRequest{
			Index:    1,
			Size:     len(respSimCards.List),
			Status:   utils.GetValuePointer(int(model.RepaidStatusDelay)),
			EbikeIds: ebikeIds,
		}, model.QueryOptions{OnlyList: true})
		if err != nil {
			return err
		} else {
			for _, v := range respPrepaidList {
				preRequests.Items = append(preRequests.Items, &model.PreRechargeItem{
					PrepaidId: utils.GetValuePointer(v.Id),
					EbikeId:   utils.GetValuePointer(v.EbikeId),
				})
			}
		}
		if len(preRequests.Items) <= 0 {
			break
		}

		if err = SimCard.PreRecharge(ctx, preRequests); err != nil {
			log.Errorf("prepaid recharge err: %+v", err)
			return err
		}

		if len(respSimCards.List) < size {
			break
		}

		index = index + 1
	}
	return nil
}

// Update
func (a *prepaid) Update(ctx context.Context, in *model.PrepaidUpdateRequest) error {
	var (
		dict = make(map[string]interface{})
	)

	if in.Id != nil {
		dict["id"] = in.Id
	}

	if in.PaymentId != nil {
		dict["payment_id"] = in.PaymentId
	}

	if in.EbikeId != nil {
		dict["ebike_id"] = in.EbikeId
	}

	if in.Sn != nil {
		dict["sn"] = in.Sn
	}

	if in.EffectiveDate != nil {
		dict["effective_date"] = in.EffectiveDate
	}

	if in.ExpiredDate != nil {
		dict["expired_date"] = in.ExpiredDate
	}

	if in.Status != nil {
		dict["status"] = in.Status
	}

	if in.Source != nil {
		dict["source"] = in.Source
	}

	if in.CreatedAt != nil {
		dict["created_at"] = in.CreatedAt
	}

	if in.UpdatedAt != nil {
		dict["updated_at"] = in.UpdatedAt
	}

	// do other update here
	updateAt := time.Now().Unix()
	in.UpdatedAt = &updateAt
	return a.iPrepaid.Update(ctx, *in.Id, dict)
}

// Delete
func (a *prepaid) Delete(ctx context.Context, in *model.PrepaidDeleteRequest) error {
	return a.iPrepaid.Delete(ctx, in.Id)
}

// List
func (a *prepaid) List(ctx context.Context, in *model.PrepaidListRequest) (*model.PrepaidListResponse, error) {
	var (
		err   error
		total int
		list  []*entity.Prepaid
		out   = &model.PrepaidListResponse{}
	)

	if total, list, err = a.iPrepaid.List(ctx, in); err != nil {
		return nil, err
	}

	out.Total = total
	out.List = mapping.PrepaidsEntityToDto(list)

	return out, nil
}

// Find
func (a *prepaid) Find(ctx context.Context, in *model.PrepaidInfoRequest) (*model.PrepaidInfo, error) {
	var (
		err  error
		data *entity.Prepaid
		out  = &model.PrepaidInfo{}
	)

	if data, err = a.iPrepaid.Find(ctx, in); err != nil {
		return nil, err
	}

	out = mapping.PrepaidEntityToDto(data)
	return out, nil
}

// buildPrepaid build entity
func buildPrepaid(in *model.PrepaidCreateRequest) *entity.Prepaid {

	ety := &entity.Prepaid{}

	ety.Id = in.Id

	ety.PaymentId = in.PaymentId

	ety.EbikeId = in.EbikeId

	ety.Sn = in.Sn

	ety.Status = int(model.RepaidStatusDelay)

	ety.Source = in.Source
	if in.Source == 0 {
		ety.Source = int(model.RepaidSourceApp)
	}

	ety.CreatedAt = time.Now().Unix()

	ety.UpdatedAt = time.Now().Unix()

	return ety
}
